package com.wk.design.template;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * 利用模板模式重构JdbcTemplate
 */
public abstract class JdbcTemplate {
    private DataSource dataSource;
    public JdbcTemplate(DataSource dataSource){
        this.dataSource = dataSource;
    }

    /**
     * 执行查询
     * @param sql
     * @param rowMapper 查询结果封装，函数式接口
     * @param values 查询参数
     * @return
     */
    public List<?> excuteQuery(String sql,RowMapper<?> rowMapper,Object[] values){
        try {
            //获取连接
            Connection connection = this.getConnection();
            //创建语句集
            PreparedStatement pstm = this.createPreparedStatement(connection,sql);
            // 执行语句集
            ResultSet rs = this.executeQuery(pstm,values);
            // 处理结果集
            List<?> result = this.parseResultSet(rs,rowMapper);
            // 关闭结果集
            this.closeResultSet(rs);
            //关闭语句集
            this.closePreparedStatement(pstm);
            // 关闭连接
            this.closeConnection(connection);
            return result;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private void closeConnection(Connection connection) throws SQLException {
        connection.close();
    }

    protected void closePreparedStatement(PreparedStatement pstm) throws SQLException {
        pstm.close();
    }

    private void closeResultSet(ResultSet rs) throws SQLException {
        rs.close();
    }

    private List<?> parseResultSet(ResultSet rs, RowMapper<?> rowMapper) throws Exception {
        //类A是类B的父类，G<A>和G<B>是没有关系的，二者共同的父类是G<?>
        //结论：添加(写入)对于List<?>不能向其内部添加数据，除了添加null
        //结论：获取(读取)对于List<?>允许读取数据，读取的数据类型为Object
        List<Object> result = new ArrayList<>();
        int rowNum = 1;
        while (rs.next()){
            result.add(rowMapper.mapRow(rs,rowNum++));
        }
        return result;
    }

    private ResultSet executeQuery(PreparedStatement pstm, Object[] values) throws SQLException {
        // 插入参数
        for (int i=0;i<values.length;i++){
            pstm.setObject(i,values[i]);
        }
        return pstm.executeQuery();
    }

    private PreparedStatement createPreparedStatement(Connection connection, String sql) throws SQLException {
        return connection.prepareStatement(sql);
    }

    private Connection getConnection() throws SQLException {
        return this.dataSource.getConnection();
    }
}
